#!/usr/bin/python


"""
    File:       BrowserDriver.py
    Brief:      Extend webDriver
    Author:     Xu Qiang
    Version:    1.0
    Date:       2017 - 4 - 18
"""


import time
import Static
import selenium
from ServiceControl import killProcessTreeWithName, ServiceState
from selenium import webdriver
from Error import TimeOut

class BrowserType( object ):
    '''
    Static Enum class, define all supported browser types.
    '''
    IE11 = "ie11"
    Chrome = "chrome"
    FireFox = "firefox"
    Chrome_360 ="360"


class WebEventListener( object ):
    '''
    Static class, define a hook to webdriver.execute method, after webdriver.execute \
    is called, wait for webpage is ready.
    '''
    @staticmethod
    def _isReady( driver ):
        from selenium.common.exceptions import NoAlertPresentException
        driver._eventFireFlag = False
        result = True
        try:
            #iedriver will close modal dialog when execute script
            if isinstance( driver, webdriver.Ie ):
                text = driver.switch_to_alert().text
                from Log import LogW
                LogW.write( "Alert present: %s" % text )
            else:
                raise NoAlertPresentException()
        except NoAlertPresentException:
            try:
#                 result = driver.execute_script( 'return document.readyState =="complete";' )
                containJquery = driver.execute_script( 'return Boolean(window.jQuery);' )
                if containJquery:
                    result = driver.execute_script( 'return document.readyState =="complete" && jQuery.active == 0;' )
                else:
                    result = driver.execute_script( 'return document.readyState =="complete";' )
            except Exception, e:
                if "Modal dialog present" in str( e ) or "JavaScript error" in str( e ) or "open modal dialog blocked" in str( e ):
                    result = True
                else:
                    result = False
        driver._eventFireFlag = True
        return result

    @staticmethod
    def waitForWebReady( driver , cmd ):
        for i in range( int( Static.testEnvtConf.Timeout ) ):
            if WebEventListener._isReady( driver ):
                return
            time.sleep( 1 )
        raise TimeOut( "TimeOut: webpage not loaded after action %s in %s seconds." % ( str( cmd ), Static.testEnvtConf.Timeout ) )

    @staticmethod
    def before_execute( driver ):
        pass#time.sleep( 0.1 )


    @staticmethod
    def after_execute( driver, driver_command ):
        from selenium.webdriver.remote.command import Command
        needWaitCommand = [ Command.SEND_KEYS_TO_ELEMENT, \
            Command.CLICK_ELEMENT, Command.REFRESH, \
            Command.GO_BACK, Command.GO_FORWARD, Command.CLICK]
        if driver_command == Command.GET:
            time.sleep( 0.1 )

        if driver.state == ServiceState.Active and driver._eventFireFlag and driver_command in needWaitCommand:
            WebEventListener.waitForWebReady( driver, driver_command )


    @staticmethod
    def on_exception( exception, driver ):
        print exception


class _IE( webdriver.Ie ):
    '''
    IE driver service, reload method execute, make execute as asynchronous method
    '''
    def __init__( self ):
        self._state = ServiceState.Killed
        self._eventFireFlag = True
        self._result = None

    def __del__( self ):
        self.stop()

    def _executeThread( self, driver_command, params ):
        try:
            self._result = super( _IE, self ).execute( driver_command, params )
        except Exception, e:
            self._result = e

    def execute( self, driver_command, params = None ):
        self._result = None
        WebEventListener.before_execute( self )
        try:
            import threading
            th = threading.Thread( None, self._executeThread, args = ( driver_command, params ) )
            th.start()
            th.join( int( Static.testEnvtConf.Timeout ) )
        except Exception, e:
            WebEventListener.on_exception( e, self )
        if th.isAlive():
            raise TimeOut( "TimeOut: Action %s not return from IEDriver after %s seconds." % ( driver_command, Static.testEnvtConf.Timeout ) )

        if isinstance( self._result, Exception ):
            raise self._result

        WebEventListener.after_execute( self, driver_command )

        return self._result

    def start( self ):
        if self._state == ServiceState.Killed:
            self.__init__()
            try:
                super( _IE, self ).__init__( Static.installEnvConf.IEDriverServer )
            except Exception, e:
                self._state = ServiceState.Killed
                return False
            try:
                super( _IE, self ).implicitly_wait( int( Static.testEnvtConf.Timeout ) )
                super( _IE, self ).set_page_load_timeout( int( Static.testEnvtConf.Timeout ) )
            except Exception, e:
                pass
            self._state = ServiceState.Active
            return True

    def close( self ):
        super( _IE, self ).close()
        allhandles = self.window_handles
        if len( allhandles ) != 0:
            self.switch_to_window( allhandles[0] )
        else:
            self.quit()

    def quit( self ):
        self.stop()

    def stop( self ):
        try:
            if self.state == ServiceState.Active:
                super( _IE, self ).quit()
        except Exception, e:
            pass
        finally:
            self._state = ServiceState.Killed
        try:
            killProcessTreeWithName( Static.installEnvConf.IEDriverServer )
        except Exception, e:
            pass
        try:
            killProcessTreeWithName( Static.installEnvConf.IE )
        except Exception, e:
            pass

    def restart( self ):
        self.stop()
        import time
        time.sleep( 1 )
        return self.start()

    @property
    def state( self ):
        return self.getState()

    def getState( self ):
        return self._state


class _chrome( webdriver.Chrome ):
    '''
    IE driver service, reload method execute, make execute as asynchronous method
    '''
    def __init__( self ):
        self._state = ServiceState.Killed
        self._eventFireFlag = True
        self._result = None
        self.opts = None
        opts = webdriver.ChromeOptions()
        if(Static.installEnvConf.chromePath !=None):
            opts.binary_location = Static.installEnvConf.chromePath
        self.opts = opts

#         driver = webdriver.Chrome(chrome_options=opts)

    def __del__( self ):
        self.stop()

    def _executeThread( self, driver_command, params ):
        try:
            self._result = super( _chrome, self ).execute( driver_command, params )
        except Exception, e:
            self._result = e

    def execute( self, driver_command, params = None ):
        self._result = None
        WebEventListener.before_execute( self )
        try:
            import threading
            th = threading.Thread( None, self._executeThread, args = ( driver_command, params ) )
            th.start()
            th.join( int( Static.testEnvtConf.Timeout ) )
        except Exception, e:
            WebEventListener.on_exception( e, self )
        if th.isAlive():
            raise TimeOut( "TimeOut: Action %s not return from chromeDriver after %s seconds." % ( driver_command, Static.testEnvtConf.Timeout ) )

        if isinstance( self._result, Exception ):
            raise self._result

        WebEventListener.after_execute( self, driver_command )

        return self._result

    def start( self ):
        if self._state == ServiceState.Killed:
            self.__init__()
            try:
                super( _chrome, self ).__init__( Static.installEnvConf.chromeDriver,chrome_options=self.opts)
            except Exception, e:
                self._state = ServiceState.Killed
                return False
            try:
                super( _chrome, self ).implicitly_wait( int( Static.testEnvtConf.Timeout ) )
                super( _chrome, self ).set_page_load_timeout( int( Static.testEnvtConf.Timeout ) )
            except Exception, e:
                pass
            self._state = ServiceState.Active
            return True

    def close( self ):
        super( _chrome, self ).close()
        allhandles = self.window_handles
        if len( allhandles ) != 0:
            self.switch_to_window( allhandles[0] )
        else:
            self.quit()

    def quit( self ):
        self.stop()

    def stop( self ):
        try:
            if self.state == ServiceState.Active:
                super( _chrome, self ).quit()
        except Exception, e:
            pass
        finally:
            self._state = ServiceState.Killed
        try:
            killProcessTreeWithName( Static.installEnvConf.chromeDriver )
        except Exception, e:
            pass
        try:
            killProcessTreeWithName( Static.installEnvConf.chrome )
        except Exception, e:
            pass

    def restart( self ):
        self.stop()
        import time
        time.sleep( 1 )
        return self.start()

    @property
    def state( self ):
        return self.getState()

    def getState( self ):
        return self._state


class _firefox( webdriver.Firefox ):
    '''
    Firefox driver service, reload method execute, make execute as asynchronous method
    '''
    def __init__( self ):
        self._state = ServiceState.Killed
        self._eventFireFlag = True
        self._result = None

    def __del__( self ):
        self.stop()

    def _executeThread( self, driver_command, params ):
        try:
            self._result = super( _firefox, self ).execute( driver_command, params )
        except Exception, e:
            self._result = e

    def execute( self, driver_command, params = None ):
        self._result = None
        WebEventListener.before_execute( self )
        try:
            import threading
            th = threading.Thread( None, self._executeThread, args = ( driver_command, params ) )
            th.start()
            th.join( int( Static.testEnvtConf.Timeout ) )
        except Exception, e:
            WebEventListener.on_exception( e, self )
        if th.isAlive():
            raise TimeOut( "TimeOut: Action %s not return from IEDriver after %s seconds." % ( driver_command, Static.testEnvtConf.Timeout ) )

        if isinstance( self._result, Exception ):
            raise self._result

        WebEventListener.after_execute( self, driver_command )

        return self._result

    def start( self ):
        if self._state == ServiceState.Killed:
            self.__init__()
            try:
                super( _firefox, self ).__init__()
            except Exception, e:
                self._state = ServiceState.Killed
                return False
            try:
                super( _firefox, self ).implicitly_wait( int( Static.testEnvtConf.Timeout ) )
                super( _firefox, self ).set_page_load_timeout( int( Static.testEnvtConf.Timeout ) )
            except Exception, e:
                pass
            self._state = ServiceState.Active
            return True

    def close( self ):
        super( _firefox, self ).close()
        allhandles = self.window_handles
        if len( allhandles ) != 0:
            self.switch_to_window( allhandles[0] )
        else:
            self.quit()

    def quit( self ):
        self.stop()

    def stop( self ):
        try:
            if self.state == ServiceState.Active:
                super( _firefox, self ).quit()
        except Exception, e:
            pass
        finally:
            self._state = ServiceState.Killed
        try:
            killProcessTreeWithName( Static.installEnvConf.firefox )
        except Exception, e:
            pass

    def restart( self ):
        self.stop()
        import time
        time.sleep( 1 )
        return self.start()

    @property
    def state( self ):
        return self.getState()

    def getState( self ):
        return self._state


class _360chrome( webdriver.Chrome ):
    '''
    360 driver service, reload method execute, make execute as asynchronous method
    '''
    def __init__( self ):
        self._state = ServiceState.Killed
        self._eventFireFlag = True
        self._result = None
        self.opts = None
        opts = webdriver.ChromeOptions()
#         opts.binary_location = "C:\\Users\\Administrator\\AppData\\Roaming\\360se6\\Application\\360se.exe"
        opts.binary_location = Static.installEnvConf.chrome360sePath
        self.opts = opts

#         driver = webdriver.Chrome(chrome_options=opts)

    def __del__( self ):
        self.stop()

    def _executeThread( self, driver_command, params ):
        try:
            self._result = super( _360chrome, self ).execute( driver_command, params )
        except Exception, e:
            print e
            self._result = e

    def execute( self, driver_command, params = None ):
        self._result = None
        WebEventListener.before_execute( self )
        try:
            import threading
            th = threading.Thread( None, self._executeThread, args = ( driver_command, params ) )
            th.start()
            th.join( int( Static.testEnvtConf.Timeout ) )
        except Exception, e:
            WebEventListener.on_exception( e, self )
        if th.isAlive():
            raise TimeOut( "TimeOut: Action %s not return from chromeDriver after %s seconds." % ( driver_command, Static.testEnvtConf.Timeout ) )

        if isinstance( self._result, Exception ):
            raise self._result

        WebEventListener.after_execute( self, driver_command )

        return self._result

    def start( self ):
        if self._state == ServiceState.Killed:
            self.__init__()
            try:
                super( _360chrome, self ).__init__( Static.installEnvConf.chrome360seDriverPath,chrome_options=self.opts)
            except Exception, e:
                self._state = ServiceState.Killed
                return False
            try:
                super( _360chrome, self ).implicitly_wait( int( Static.testEnvtConf.Timeout ) )
                super( _360chrome, self ).set_page_load_timeout( int( Static.testEnvtConf.Timeout ) )
            except Exception, e:
                pass
            self._state = ServiceState.Active
            return True

    def close( self ):
        super( _360chrome, self ).close()
        allhandles = self.window_handles
        if len( allhandles ) != 0:
            self.switch_to_window( allhandles[0] )
        else:
            self.quit()

    def quit( self ):
        self.stop()

    def stop( self ):
        try:
            if self.state == ServiceState.Active:
                super( _360chrome, self ).quit()
        except Exception, e:
            pass
        finally:
            self._state = ServiceState.Killed
        try:
            killProcessTreeWithName( Static.installEnvConf.chrome360seDriver )
        except Exception, e:
            pass
        try:
            killProcessTreeWithName( Static.installEnvConf.chrome360se )
        except Exception, e:
            pass

    def restart( self ):
        self.stop()
        import time
        time.sleep( 1 )
        return self.start()

    @property
    def state( self ):
        return self.getState()

    def getState( self ):
        return self._state


class _safari( webdriver.Safari ):
    '''
    360 driver service, reload method execute, make execute as asynchronous method
    '''
    def __init__( self ):
        self._state = ServiceState.Killed
        self._eventFireFlag = True
        self._result = None
 

    def __del__( self ):
        self.stop()

    def _executeThread( self, driver_command, params ):
        try:
            self._result = super( _safari, self ).execute( driver_command, params )
        except Exception, e:
            print e
            self._result = e

    def execute( self, driver_command, params = None ):
        self._result = None
        WebEventListener.before_execute( self )
        try:
            import threading
            th = threading.Thread( None, self._executeThread, args = ( driver_command, params ) )
            th.start()
            th.join( int( Static.testEnvtConf.Timeout ) )
        except Exception, e:
            WebEventListener.on_exception( e, self )
        if th.isAlive():
            raise TimeOut( "TimeOut: Action %s not return from chromeDriver after %s seconds." % ( driver_command, Static.testEnvtConf.Timeout ) )

        if isinstance( self._result, Exception ):
            raise self._result

        WebEventListener.after_execute( self, driver_command )

        return self._result

    def start( self ):
        if self._state == ServiceState.Killed:
            self.__init__()
            try:
                super( _safari, self ).__init__( Static.installEnvConf.chromeDriver,chrome_options=self.opts)
            except Exception, e:
                self._state = ServiceState.Killed
                return False
            try:
                super( _safari, self ).implicitly_wait( int( Static.testEnvtConf.Timeout ) )
                super( _safari, self ).set_page_load_timeout( int( Static.testEnvtConf.Timeout ) )
            except Exception, e:
                pass
            self._state = ServiceState.Active
            return True

    def close( self ):
        super( _safari, self ).close()
        allhandles = self.window_handles
        if len( allhandles ) != 0:
            self.switch_to_window( allhandles[0] )
        else:
            self.quit()

    def quit( self ):
        self.stop()

    def stop( self ):
        try:
            if self.state == ServiceState.Active:
                super( _safari, self ).quit()
        except Exception, e:
            pass
        finally:
            self._state = ServiceState.Killed
        try:
            killProcessTreeWithName( Static.installEnvConf.chromeDriver )
        except Exception, e:
            pass
        try:
            killProcessTreeWithName( Static.installEnvConf.chrome )
        except Exception, e:
            pass

    def restart( self ):
        self.stop()
        import time
        time.sleep( 1 )
        return self.start()

    @property
    def state( self ):
        return self.getState()

    def getState( self ):
        return self._state


def BrowserDriver( browserType ):
    '''
    webdriver factory, create webdriver instance according the given type
    @param browserType: browser type, all support type are defined in classs @see: BrowserType
    '''
    if browserType == BrowserType.IE11:
        return _IE()
    elif browserType == BrowserType.Chrome:
        return _chrome()
    elif browserType == BrowserType.FireFox:
        return _firefox()
    elif browserType == BrowserType.Chrome_360:
        return _360chrome()
    else:
        return None
